------------------------------------------------------------------------------
-- Mining script for computercraft's turtles by Hea3veN
-- Version 0.3
------------------------------------------------------------------------------
loadfile("lib/status.lua")()
loadfile("lib/state_machine.lua")()
os.loadAPI("api/turtle_utils")
os.loadAPI("api/logf")
os.loadAPI("api/inventory")

FACE_NORTH = 1
FACE_SOUTH = 3
FACE_EAST = 2
FACE_WEST = 0

function d()
	return statusGetProgData()
end

function atOddZ()
	return (d().miningZ % 2) == 1
end

function getRealMiningX()
	return ((d().chunk_no-1) % 8) * d().chunk_size + 1 + d().miningX-1
end

function getRealMiningZ()
	return math.floor((d().chunk_no - 1) / 8) * d().chunk_size + 3 + d().miningZ-1
end

function getRequiredFuelLevel()
	return 16 + getRealMiningZ() * 2 + getRealMiningX() * 2 + d().chunk_size * d().chunk_size * d().depth
end

function turnRight()
	turtle.turnRight()
	d().facing = d().facing + 1
	if d().facing > 3 then
		d().facing = 0
	end
	logf.logf(logf.DEBUG, "mine3-movement", "Turn right, now facing "..tostring(d().facing))
end

function turnLeft()
	turtle.turnLeft()
	d().facing = d().facing - 1
	if d().facing < 0 then
		d().facing = 3
	end
	logf.logf(logf.DEBUG, "mine3-movement", "Turn left, now facing "..tostring(d().facing))
end

function forward(doDig)
	if turtle_utils.forward(1, doDig) then
		if d().facing == FACE_NORTH then
			d().z = d().z + 1
			if doDig then
				d().miningZ = d().miningZ + 1
			end
		elseif d().facing == FACE_SOUTH then
			d().z = d().z - 1
			if doDig then
				d().miningZ = d().miningZ - 1
			end
		elseif d().facing == FACE_EAST then
			d().x = d().x + 1
			if doDig then
				d().miningX = d().miningX + 1
			end
		elseif d().facing == FACE_WEST then
			d().x = d().x - 1
			if doDig then
				d().miningX = d().miningX - 1
			end
		end
		logf.logf(logf.DEBUG, "mine3-movement", "Forward, now at ("..tostring(d().x)..", "..tostring(d().y)..", "..tostring(d().z)..")")
		return true
	else
		return false
	end
end

function back(doDig)
	if turtle_utils.back(1) then
		if d().facing == FACE_NORTH then
			d().z = d().z - 1
		elseif d().facing == FACE_SOUTH then
			d().z = d().z + 1
		elseif d().facing == FACE_EAST then
			d().x = d().x - 1
		elseif d().facing == FACE_WEST then
			d().x = d().x + 1
		end
		logf.logf(logf.DEBUG, "mine3-movement", "Back, now at ("..tostring(d().x)..", "..tostring(d().y)..", "..tostring(d().z)..")")
		return true
	else
		return false
	end
end

function up(doDig)
	if turtle_utils.up(1, doDig) then
		d().y = d().y - 1
		logf.logf(logf.DEBUG, "mine3-movement", "Up, now at ("..tostring(d().x)..", "..tostring(d().y)..", "..tostring(d().z)..")")
		return true
	else
		return false
	end
end
	
function down(doDig)
	if turtle_utils.down(1, doDig) then
		d().y = d().y + 1
		logf.logf(logf.DEBUG, "mine3-movement", "Down, now at ("..tostring(d().x)..", "..tostring(d().y)..", "..tostring(d().z)..")")
		return true
	else
		return false
	end
end
	
progStates = {
	init=function()
		d().x = 1
		d().z = -2
		d().y = 1
		d().miningX = 1
		d().miningZ = 1
		d().facing = FACE_NORTH
		statusSave()
		return 'doRefuel'
	end,
	
	initStep1=function()
		if d().facing == FACE_SOUTH then
			return 'initStep1_1'
		end
		turnRight()
		return 'initStep1'
	end,
	initStep1_1=function()
		forward(false)
		return 'initStep2'
	end,
	initStep2=function()
		turnRight()
		return 'initStep3'
	end,
	initStep3=function()
		forward(false)
		return 'initStep4'
	end,
	initStep4=function()
		forward(false)
		return 'initStep5'
	end,
	initStep5=function()
		turnRight()
		return 'initStep6'
	end,
	initStep6=function()
		forward(false)
		return 'initStep7'
	end,
	initStep7=function()
		forward(false)
		return 'initStep8'
	end,
	initStep8=function()
		forward(false)
		return 'initStep9'
	end,
	initStep9=function()
		forward(false)
		return 'initStep10'
	end,
	initStep10=function()
		turnRight()
		return 'initStep11'
	end,
	initStep11=function()
		forward(false)
		return 'initStep12'
	end,
	initStep12=function()
		forward(false)
		return 'initStep13'
	end,
	initStep13=function()
		turnLeft()
		return 'positionZ'
	end,
	
	-- Going to mining position
	positionZ=function()
		if d().z >= getRealMiningZ() then
			return 'turnReachedMiningZ'
		end
		forward(false)
		return 'positionZ'
	end,
	turnReachedMiningZ=function()
		turnRight()
		return 'positionX'
	end,
	positionX=function()
		if d().x >= getRealMiningX() then
			return 'reachedMiningX'
		end
		forward(false)
		return 'positionX'
	end,
	reachedMiningX=function()
		if atOddZ() then
			return 'goDown'
		else
			turnLeft()
			return 'reachedMiningX2'
		end
	end,
	reachedMiningX2=function()
		turnLeft()
		return 'goDown'
	end,
	--
	
	--
	forward=function()
		if atOddZ() then
			if d().miningX >= d().chunk_size then
				turnLeft()
				return 'turnStep1'
			end
		else
			if d().miningX <= 1 then
				turnRight()
				return 'turnStep1'
			end
		end
		if not forward(true) then
			return 'end'
		end
		
		if d().y <= 1 then
			return 'goDown'
		else
			return 'goUp'
		end
	end,
	turnStep1=function()
		if d().miningZ >= d().chunk_size then
			return 'returnHomeGoUp'
		end
		
		if not forward(true) then
			return 'end'
		end
		return 'turnStep2'
	end,
	turnStep2=function()
		if atOddZ() then
			turnRight()
		else
			turnLeft()
		end
		if d().y <= 1 then
			return 'goDown'
		else
			return 'goUp'
		end
	end,
	--
	
	-- Actual mining
	goDown=function()
		if d().y >= d().depth then
			return 'forward'
		end
		if not down(true) then
			return 'end'
		end
		
		if inventory.inventoryIsFull() then
			return 'returnHomeGoUp'
		end
		return 'goDown'
	end,
	goUp=function()
		if d().y <= 1 then
			return 'forward'
		end
		if not up(true) then
			return 'end'
		end
		
		if inventory.inventoryIsFull() then
			return 'beginReturnHomeIndirect'
		end
		return 'goUp'
	end,
	--
	
	-- Return to home
	returnHomeGoUp=function()
		if d().y <= 0 then
			return 'returnHomeReorient'
		end
		if not up(false) then
			return 'end'
		end
		return 'returnHomeGoUp'
	end,
	beginReturnHomeIndirect=function()
		if atOddZ() then
			if d().miningX > 1 then
				back()
				return 'returnHomeGoUp'
			else
				turnLeft()
				return 'returnHomeBackOne'
			end
		else
			if d().miningX < d().chunk_size then
				back()
				return 'returnHomeGoUp'
			else
				turnRight()
				return 'returnHomeBackOne'
			end
		end
	end,
	returnHomeBackOne=function()
		if not (d().miningX == 1 and d().miningZ == 1) then
			back()
		end
		return 'returnHomeGoUp'
	end,
	returnHomeReorient=function()
		if d().facing == FACE_WEST then
			return 'returnHomePositionX'
		end
		turnLeft()
		return 'returnHomeReorient'
	end,
	returnHomePositionX=function()
		if d().x <= 1 then
			return 'returnHomeReachedX'
		end
		forward(false)
		return 'returnHomePositionX'
	end,
	returnHomeReachedX=function()
		turnLeft()
		return 'returnHomePositionZ'
	end,
	returnHomePositionZ=function()
		if d().z <= 1 then
			return 'returnHomeReachedZ'
		end
		forward(false)
		return 'returnHomePositionZ'
	end,
	returnHomeReachedZ=function()
		down()
		return 'dropInventory'
	end,
	dropInventory=function()
		for i=1,16 do
			turtle.select(i)
			while turtle.getItemCount(i) > 0 do
				if not turtle.drop() then
					print("Could not place the items in the chest, waiting for space.")
					os.sleep(5)
				end
				os.sleep(1)
			end
		end
		turtle.select(1)
		if d().miningX <= 1 and d().miningZ >= d().chunk_size then
			return 'goRefuel'
		else
			if turtle.getFuelLevel() <= getRequiredFuelLevel() then
				return 'goRefuel'
			else
				return 'homeTurnBack'
			end
		end
	end,
	homeTurnBack=function()
		turnLeft()
		return 'homeTurnBack2'
	end,
	homeTurnBack2=function()
		turnLeft()
		return 'positionZ'
	end,
	goRefuel=function()
		turnLeft()
		return 'goRefuel2'
	end,
	goRefuel2=function()
		forward(false)
		return 'goRefuel3'
	end,
	goRefuel3=function()
		forward(false)
		return 'goRefuel4'
	end,
	goRefuel4=function()
		turnRight()
		return 'goRefuel5'
	end,
	goRefuel5=function()
		forward(false)
		return 'goRefuel6'
	end,
	goRefuel6=function()
		forward(false)
		return 'goRefuel7'
	end,
	goRefuel7=function()
		forward(false)
		return 'goRefuel8'
	end,
	goRefuel8=function()
		turnRight()
		return 'goRefuel9'
	end,
	goRefuel9=function()
		forward(false)
		return 'goRefuel10'
	end,
	goRefuel10=function()
		forward(false)
		return 'goRefuel11'
	end,
	goRefuel11=function()
		turnRight()
		return 'doRefuel'
	end,
	doRefuel=function()
		required = getRequiredFuelLevel()
		while turtle.getFuelLevel() < required do
			turtle.select(1)
			if not turtle.suckUp() then
				os.sleep(10)
			end
			turtle.refuel()
		end
		if d().miningX <= 1 and d().miningZ >= d().chunk_size then
			statusEnd()
			shell.run('reboot')
		else
			return 'initStep1'
		end
	end
}

arg = { ... }

if (arg[1] == nil) then
	if not statusLoad() then
		print("Usage: clear chunk_size depth chunk_no")
	else
		smStart(progStates)
	end
elseif (arg[2] == nil) or (arg[3] == nil) then
	print("Usage: clear x z y\n\tClear an area of x width, z depth and y height")
else

	chunk_size = tonumber(arg[1])
	depth = tonumber(arg[2])
	chunk_no = tonumber(arg[3])

	if not statusLoad() then
		statusInit('mine3')

		d().chunk_size = chunk_size
		d().depth = depth
		d().chunk_no = chunk_no
		statusSave()
	end
	
	smStart(progStates)
end